package models

import (
	"errors"
	"fmt"
	"qysoft/libs/cache"
	"qysoft/libs/params"
	"reflect"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

// Bulletin 公告
type Bulletin struct {
	Id           string        `orm:"column(id);pk"`
	Organization *Organization `orm:"rel(fk)"`
	Dept         *Dept         `orm:"rel(fk)"`
	OrgName      string        `orm:"column(org_name);null"`
	Type         int           `orm:"column(type);null"`
	Title        string        `orm:"column(title);null"`
	Content      string        `orm:"column(content);null"`
	Btime        time.Time     `orm:"column(btime);type(timestamp with time zone);null"`
	Etime        time.Time     `orm:"column(etime);type(timestamp with time zone);null"`
	Clicks       int           `orm:"column(clicks);null"`
	Lng          float64       `orm:"column(lng);null"`
	Lat          float64       `orm:"column(lat);null"`
	Address      string        `orm:"column(address);null"`
	Status       int           `orm:"column(status);default(1)"`
	Isdelete     int           `orm:"column(isdelete);default(0)"`
	Ctime        time.Time     `orm:"column(ctime);type(timestamp with time zone);auto_now_add"`
	Creater      string        `orm:"column(creater);default('admin')"`
	Utime        time.Time     `orm:"column(utime);type(timestamp with time zone);auto_now"`
	Updater      string        `orm:"column(updater);default('admin')"`
}

func (t *Bulletin) TableName() string {
	return "sys_bulletin"
}

func init() {
	orm.RegisterModel(new(Bulletin))
}

// AddBulletin insert a new Bulletin into database and returns
// last inserted Id on success.
func AddBulletin(m *Bulletin) (id int64, err error) {
	o := orm.NewOrm()
	id, err = o.Insert(m)
	return
}

// GetBulletinById retrieves Bulletin by Id. Returns error if
// Id doesn't exist
func GetBulletinById(id string) (v *Bulletin, err error) {
	o := orm.NewOrm()
	v = &Bulletin{Id: id}
	if err = o.Read(v); err == nil {
		o.LoadRelated(v, "Dept")
		return v, nil
	}
	return nil, err
}

// GetAllBulletin retrieves all Bulletin matches certain condition. Returns empty list if
// no records exist
func GetAllBulletin(query map[string]interface{}, fields []string, sortby []string, order []string,
	offset int64, limit int64) (ml []interface{}, count int64, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(Bulletin))
	// query k=v
	for k, v := range query {
		// rewrite dot-notation to Object__Attribute
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	// order by:
	var sortFields []string
	if len(sortby) != 0 {
		if len(sortby) == len(order) {
			// 1) for each sort field, there is an associated order
			for i, v := range sortby {
				orderby := ""
				if order[i] == "desc" {
					orderby = "-" + v
				} else if order[i] == "asc" {
					orderby = v
				} else {
					return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
			qs = qs.OrderBy(sortFields...)
		} else if len(sortby) != len(order) && len(order) == 1 {
			// 2) there is exactly one order, all the sorted fields will be sorted by this order
			for _, v := range sortby {
				orderby := ""
				if order[0] == "desc" {
					orderby = "-" + v
				} else if order[0] == "asc" {
					orderby = v
				} else {
					return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
		} else if len(sortby) != len(order) && len(order) != 1 {
			return nil, 0, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
		}
	} else {
		if len(order) != 0 {
			return nil, 0, errors.New("Error: unused 'order' fields")
		}
	}

	var l []orm.Params
	qs = qs.RelatedSel().OrderBy(sortFields...)
	count, err = qs.Count()
	if _, err = qs.Limit(limit, offset).Values(&l, fields...); err == nil {
		if len(fields) == 0 {
			for _, v := range l {
				u, _ := GetBulletinById(v["Id"].(string))
				v["Tname"] = params.GetBulletinType(int(v["Type"].(int64)))
				v["Dname"] = u.Dept.Name
				ml = append(ml, v)
			}
		} else {
			// trim unused fields
			for _, v := range l {
				m := make(map[string]interface{})
				val := reflect.ValueOf(v)
				for _, fname := range fields {
					m[fname] = val.FieldByName(fname).Interface()
				}
				ml = append(ml, m)
			}
		}
		return ml, count, nil
	}
	return nil, 0, err
}

// UpdateBulletinById updates Bulletin by Id and returns error if
// the record to be updated doesn't exist
func UpdateBulletinById(m *Bulletin) (err error) {
	o := orm.NewOrm()
	v := Bulletin{Id: m.Id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Update(m); err == nil {
			fmt.Println("Number of records updated in database:", num)
		}
	}
	return
}

// DeleteBulletin deletes Bulletin by Id and returns error if
// the record to be deleted doesn't exist
func DeleteBulletin(id string) (err error) {
	o := orm.NewOrm()
	v := Bulletin{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Delete(&Bulletin{Id: id}); err == nil {
			fmt.Println("Number of records deleted in database:", num)
		}
	}
	return
}

// SoftDeleteBulletin 软删除公告 Bulletin by Id and returns error if
// the record to be deleted doesn't exist
func SoftDeleteBulletin(id string, uname string) (err error) {
	o := orm.NewOrm()
	v := Bulletin{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		v.Isdelete = 1
		v.Updater = uname
		v.Utime = time.Now()
		_, err = o.Update(&v)
	}
	return
}

//GetValidationBulletins 根据单位获取生效公告
//Param orgId 单位id
//return []Bulletin,error
func GetValidationBulletins(orgId int64) ([]Bulletin, error) {
	o := orm.NewOrm()
	sql := "SELECT * FROM public.sys_bulletin ta WHERE ta.btime <= now() AND ta.etime > now() AND ta.org_id=? OR ta.type=1"
	var bs []Bulletin
	_, err := o.Raw(sql, orgId).QueryRows(&bs)
	return bs, err
}

//GetBulletinsByUser 根据用户获取生效公告
//Param user user
//return []Bulletin
func GetBulletinsByUser(user User) []Bulletin {
	cid := "GetBulletinsByUser.id." + user.Id
	var bulls []Bulletin
	err := cache.GetCache(cid, &bulls)
	if err != nil {
		o := orm.NewOrm()
		sql := "SELECT * FROM sys_bulletin ta WHERE ta.isdelete=0 and ta.btime <= now() AND ta.etime > now() AND ta.organization_id=? and ta.type=0"
		var bs, bs1, bs2 []Bulletin
		o.Raw(sql, user.Organization.Id).QueryRows(&bs)
		bulls = append(bulls, bs...)
		sql1 := "SELECT * FROM sys_bulletin ta WHERE ta.isdelete=0 and ta.btime <= now() AND ta.etime > now() AND ta.dept_id=? and ta.type=1"
		o.Raw(sql1, user.Dept[0].Id).QueryRows(&bs1)
		bulls = append(bulls, bs1...)
		sql2 := "SELECT * FROM sys_bulletin ta WHERE ta.isdelete=0 and ta.btime <= now() AND ta.etime > now() AND ta.type=2"
		o.Raw(sql2).QueryRows(&bs2)
		bulls = append(bulls, bs2...)
		cacheExpire, _ := beego.AppConfig.Int("cache_expire")
		cache.SetCache(cid, bulls, cacheExpire)
	}

	return bulls
}
